package com.shuai.baibi.service.impl;

import cn.hutool.core.io.file.FileNameUtil;
import com.shuai.baibi.common.ErrorCode;
import com.shuai.baibi.constant.FileConstant;
import com.shuai.baibi.exception.BusinessException;
import com.shuai.baibi.model.dto.fileserver.FileServerResponseDTO;
import com.shuai.baibi.model.entity.Chart;
import com.shuai.baibi.model.entity.ChartAnnex;
import com.shuai.baibi.service.ChartAnnexService;
import com.shuai.baibi.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Service
@Slf4j
public class FileServiceImpl implements FileService {

    @Value("${file-server.url}")
    private String fileServerURL;
    @Autowired
    private ChartAnnexService chartAnnexService;

    @Autowired
    private RestTemplate restTemplate;

    @Value("${annex.storage-path}")
    private String annexStoragePath;

    /**
     * 校验用户上传的文件格式是否正确
     */
    public boolean checkFileStyle(MultipartFile multipartFile) {
        String originalFilename = multipartFile.getOriginalFilename();
        // 文件名校验
        String fileSuffix = FileNameUtil.getSuffix(originalFilename);
        if (!FileConstant.ANALYSIS_FILE_SUFFIXES.contains(fileSuffix)) {
            return false;
        }
        return multipartFile.getSize() <= FileConstant.ANALYSIS_FILE_SIZE;
    }

    /**
     * 上传文件到文件服务器，饼保存记录到附件表中
     */
    @Override
    public void uploadFile(MultipartFile file, Chart chart) throws IOException, URISyntaxException {
        String originalFilename = file.getOriginalFilename();
        int suffix = originalFilename.lastIndexOf(".");
        String storageName = UUID.randomUUID().toString().replace("-", "") + originalFilename.substring(suffix);
        ChartAnnex chartAnnex = new ChartAnnex();
        chartAnnex.setAnnexName(originalFilename);
        chartAnnex.setStorageName(storageName);
        chartAnnex.setUserId(chart.getUserId());
        chartAnnex.setIsLasted(ChartAnnex.Y);
        chartAnnex.setChartId(chart.getId());


        // 上传文件到文件服务器
        // ----------- start -----------------
        if (this.uploadFileToFileServer(file.getBytes(), storageName)) {
            // 保存记录到数据库中
            if (!chartAnnexService.save(chartAnnex)) {
                log.error("用户{}上传的文件保存失败，数据库服务异常!", chart.getUserId());
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "服务异常，正在加急修复中...");
            }
        } else {
            log.error("上传文件到文件[{}]服务器出错", storageName);
            throw new IOException("系统错误，正在紧急修复中！");
        }
        // -----------  end  -----------------


    }

    /**
     * 上传文件到本地，保存记录到附件表中
     *
     * @param file
     * @param chart
     */
    @Override
    public void uploadFileSingle(@NotNull MultipartFile file, @NotNull Chart chart) {
        String originalFilename = file.getOriginalFilename();
        int suffix = originalFilename.lastIndexOf(".");
        String storageName = UUID.randomUUID().toString().replace("-", "") + originalFilename.substring(suffix);
        ChartAnnex chartAnnex = new ChartAnnex();
        chartAnnex.setAnnexName(originalFilename);
        chartAnnex.setStorageName(storageName);
        chartAnnex.setUserId(chart.getUserId());
        chartAnnex.setIsLasted(ChartAnnex.Y);
        chartAnnex.setChartId(chart.getId());


        File existsFile = new File(annexStoragePath);
        if (!existsFile.exists()) {
            if (!existsFile.mkdirs()) {
                log.error("接收用户上传文件时创建文件持久化目录失败！" + this.getClass());
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "服务异常，正在加急修复中");
            }
        }
        try {
            // 将用户上传的文件保存到本地
            file.transferTo(new File(annexStoragePath + storageName));
            // 保存记录到数据库中
            if (!chartAnnexService.save(chartAnnex)) {
                log.error("用户{}上传的文件保存失败，数据库服务异常!", chart.getUserId());
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "服务异常，正在加急修复中...");
            }
        } catch (IOException e) {
            log.error("文件持久化出错！" + e.getMessage());
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, e.getMessage());
        }
    }

    @Override
    public void downLoadFileFromFileServer(String storageName, HttpServletResponse response) {
        log.info("下载文件【{}】开始", storageName);
        Map<String, String> uriVariables = new HashMap<>();
        uriVariables.put("storageName", storageName);
        ResponseExtractor<Boolean> responseExtractor = clientHttpResponse -> {
            // 设置响应头，直接用第三方文件服务的响应头
            HttpHeaders headers = clientHttpResponse.getHeaders();
            headers.forEach((key, value) -> response.setHeader(key, value.get(0)));
            // 收到响应输入流即时拷贝写出到响应输出流中: inputStream -> outputStream
            StreamUtils.copy(clientHttpResponse.getBody(), response.getOutputStream());
            return true;
        };
        String requestURL = fileServerURL + "/file/download?storageName=" + storageName;
        Boolean execute = restTemplate.execute(requestURL, HttpMethod.GET, null, responseExtractor, uriVariables);
        log.info("下载文件【{}】结束", storageName);
    }

    /**
     * 接受到byte[]文件数据后，调用上述的upload接口上传文件
     *
     * @param fileData        文件数据
     * @param storageFileName 文件名
     */
    private boolean uploadFileToFileServer(byte[] fileData, String storageFileName) throws URISyntaxException {
        //请求头，以表单形式提交
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        headers.add("Content-Type", MediaType.MULTIPART_FORM_DATA_VALUE);

        //请求传参
        ByteArrayResource fileAsResource = new ByteArrayResource(fileData) {
            @Override
            public String getFilename() {
                return storageFileName;
            }

            @Override
            public long contentLength() {
                return fileData.length;
            }
        };
        MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
        param.add("file", fileAsResource);
        param.add("fileStorageName", storageFileName);

        //构建uri
        URI uri = new URI(fileServerURL + "/file/upload");
        //传参
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(param, headers);
        //调用接口
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<FileServerResponseDTO> responseEntity = restTemplate.exchange(
                uri,
                HttpMethod.POST,
                requestEntity,
                new ParameterizedTypeReference<FileServerResponseDTO>() {
                });
        FileServerResponseDTO responseObj = responseEntity.getBody();
        return responseObj != null && responseObj.getCode() == 1;
    }
}
